package com.tyk.bookstore.back.storage.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tyk.bookstore.back.common.core.util.AssertUtil;
import com.tyk.bookstore.back.common.dal.mapper.dto.BinDto;
import com.tyk.bookstore.back.product.model.dto.Book4StockDto;
import com.tyk.bookstore.back.product.service.BookApiService;
import com.tyk.bookstore.back.storage.manager.excel.model.StockExcelVo;
import com.tyk.bookstore.back.storage.model.entity.StockDo;
import com.tyk.bookstore.back.storage.model.entity.WarehouseDo;
import com.tyk.bookstore.back.storage.model.query.StockAddQuery;
import com.tyk.bookstore.back.storage.model.query.StockDeleteQuery;
import com.tyk.bookstore.back.storage.model.query.StockPageQuery;
import com.tyk.bookstore.back.storage.model.query.StockUpdateQuery;
import com.tyk.bookstore.back.storage.model.result.StockResultEnum;
import com.tyk.bookstore.back.storage.model.result.WarehouseResultEnum;
import com.tyk.bookstore.back.storage.model.vo.StockPageVo;
import com.tyk.bookstore.back.storage.model.vo.StockVo;
import com.tyk.bookstore.back.storage.repository.StockRepository;
import com.tyk.bookstore.back.storage.repository.WarehouseRepository;
import com.tyk.bookstore.back.storage.service.StockService;
import com.tyk.bookstore.back.storage.util.MapStructStorage;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.math.BigInteger;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author tyk
 * @description 仓库管理
 */
@Service
@RequiredArgsConstructor
public class StockServiceImp implements StockService {

    private final StockRepository stockRepository;
    private final WarehouseRepository warehouseRepository;
    private final BookApiService bookApiService;
    private final MapStructStorage mapStructStorage;

    @Override
    public StockVo get(Long warehouseId, Long bookId) {

        AssertUtil.isTrue(
                stockRepository.exist2Eq(
                        StockDo::getWarehouseId, warehouseId,
                        StockDo::getBookId, bookId,
                        StockDo.class
                ),
                StockResultEnum.STOCK_NOT_FOUND
        );

        LambdaQueryWrapper<StockDo> w = Wrappers.lambdaQuery(StockDo.class);
        w.eq(StockDo::getWarehouseId, warehouseId)
                .eq(StockDo::getBookId, bookId);

        StockVo sv = mapStructStorage.toStockVo(
                stockRepository.selectOne(w)
        );

        String warehouseName = warehouseRepository.<String>selectSin1Eq(
                WarehouseDo::getName,
                WarehouseDo::getId, sv.getWarehouseId(),
                WarehouseDo.class
        ).getV1();
        AssertUtil.notBlank(warehouseName, WarehouseResultEnum.WAREHOUSE_NOT_FOUND);
        sv.setWarehouseName(warehouseName);

        Book4StockDto book = bookApiService.getBook4StockDtoById(sv.getBookId());
        sv.setBookName(book.getName())
                .setBookNo(book.getBookNo())
                .setIsbn(book.getIsbn());

        return sv;
    }

    @Override
    public StockPageVo getPage(StockPageQuery query) {

        // 查询
        Page<StockDo> page = new Page<>(query.getPage(), query.getLimit());
        stockRepository.selectPage(page, buildStockQueryWrapper(query));
        StockPageVo spv = mapStructStorage.toStockPageVo(page);

        // 仓库名
        Map<Long, String> wid2Wn = warehouseRepository
                .<BigInteger, String, Long>selectBinList1In(
                        WarehouseDo::getId, WarehouseDo::getName,
                        WarehouseDo::getId, spv.getRecords().stream().map(StockPageVo.Stock::getWarehouseId).collect(Collectors.toSet()),
                        WarehouseDo.class
                )
                .stream().collect(Collectors.toMap(it -> it.getV1().longValue(), BinDto::getV2));
        // 书籍信息
        Map<Long, Book4StockDto> bid2Bk = bookApiService
                .getBook4StockDtoListByIdSet(
                        spv.getRecords().stream().map(StockPageVo.Stock::getBookId).collect(Collectors.toSet())
                ).stream().collect(Collectors.toMap(Book4StockDto::getId, it -> it));

        // 补充仓库信息和书籍信息
        spv.getRecords().forEach(stock -> {
            stock.setWarehouseName(wid2Wn.get(stock.getWarehouseId()));
            Book4StockDto bs = bid2Bk.get(stock.getBookId());
            if (bs == null) return;
            stock.setBookName(bs.getName()).setBookIsbn(bs.getIsbn()).setBookNo(bs.getBookNo());
        });

        return spv;
    }

    @Override
    public void add(StockAddQuery query) {

        // 仓库存在
        AssertUtil.isTrue(
                warehouseRepository.exist1Eq(
                        WarehouseDo::getId, query.getWarehouseId(), WarehouseDo.class
                ),
                WarehouseResultEnum.WAREHOUSE_NOT_FOUND
        );

        // 书籍存在
        bookApiService.assertExistById(query.getBookId());

        // 库存不存在
        AssertUtil.isFalse(
                stockRepository.exist2Eq(
                        StockDo::getWarehouseId, query.getWarehouseId(),
                        StockDo::getBookId, query.getBookId(),
                        StockDo.class
                ),
                StockResultEnum.STOCK_ALREADY_EXISTS
        );

        // 添加
        stockRepository.insert(
                mapStructStorage.toStockDo(query)
        );
    }

    @Override
    public void update(StockUpdateQuery query) {

        // 仓库存在
        AssertUtil.isTrue(
                warehouseRepository.exist1Eq(
                        WarehouseDo::getId, query.getWarehouseId(), WarehouseDo.class
                ),
                WarehouseResultEnum.WAREHOUSE_NOT_FOUND
        );

        // 书籍存在
        bookApiService.assertExistById(query.getBookId());

        // 更新
        stockRepository.updateByWarehouseIdAndBookId(
                mapStructStorage.toStockDo(query)
        );
    }

    @Override
    public void delete(StockDeleteQuery query) {
        AssertUtil.isTrue(
                stockRepository.exist2Eq(
                        StockDo::getWarehouseId, query.getWarehouseId(),
                        StockDo::getBookId, query.getBookId(),
                        StockDo.class
                ),
                StockResultEnum.STOCK_NOT_FOUND
        );
        stockRepository.delete2Eq(
                StockDo::getWarehouseId, query.getWarehouseId(),
                StockDo::getBookId, query.getBookId(),
                StockDo.class
        );
    }

    @Override
    public void deleteBatch(List<StockDeleteQuery> queryList) {

        Long count = stockRepository.countByStockDeleteQueryList(queryList);
        AssertUtil.equal(count, Long.valueOf(queryList.size()), StockResultEnum.STOCK_NOT_FOUND);

        stockRepository.deleteBatchByStockDeleteQueryList(queryList);

    }

    @Override
    public void deleteZero() {
        stockRepository.delete1Eq(
                StockDo::getNum, 0, StockDo.class
        );
    }

    @Override
    public List<StockExcelVo> getStockExcelVoList(StockPageQuery query) {

        // 查询
        LambdaQueryWrapper<StockDo> w = buildStockQueryWrapper(query);
        List<StockDo> doList = stockRepository.selectList(w);
        List<StockExcelVo> voList = mapStructStorage.toStockExcelVo(doList);

        // 仓库名
        Map<Long, String> wid2Wn = warehouseRepository
                .<BigInteger, String, Long>selectBinList1In(
                        WarehouseDo::getId, WarehouseDo::getName,
                        WarehouseDo::getId, doList.stream().map(StockDo::getWarehouseId).collect(Collectors.toSet()),
                        WarehouseDo.class
                )
                .stream().collect(Collectors.toMap(it -> it.getV1().longValue(), BinDto::getV2));
        // 书籍信息
        Map<Long, Book4StockDto> bid2Bk = bookApiService
                .getBook4StockDtoListByIdSet(
                        doList.stream().map(StockDo::getBookId).collect(Collectors.toSet())
                ).stream().collect(Collectors.toMap(Book4StockDto::getId, it -> it));

        // 补充仓库信息和书籍信息
        voList.forEach(stock -> {
            stock.setWarehouseName(wid2Wn.get(stock.getWarehouseId()));
            Book4StockDto bs = bid2Bk.get(stock.getBookId());
            if (bs == null) return;
            stock.setBookName(bs.getName());
            stock.setBookIsbn(bs.getIsbn());
            stock.setBookNo(bs.getBookNo());
        });


        return voList;
    }

    @Override
    public void saveStockExcelVoList(List<StockExcelVo> stockList) {
        List<StockDo> doList = mapStructStorage.toStockDoList(stockList);
        stockRepository.insertBatch(doList);
    }

    /**
     * 构建库存分页查询Wrapper
     */
    private LambdaQueryWrapper<StockDo> buildStockQueryWrapper(StockPageQuery query) {
        LambdaQueryWrapper<StockDo> w = Wrappers.lambdaQuery(StockDo.class);

        w.eq(query.getWarehouseId() != null, StockDo::getWarehouseId, query.getWarehouseId());

        w.eq(query.getBookId() != null, StockDo::getBookId, query.getBookId());

        w.ge(query.getNumFrom() != null, StockDo::getNum, query.getNumFrom());
        w.le(query.getNumTo() != null, StockDo::getNum, query.getNumTo());

        w.ge(query.getCreateTimeFrom() != null, StockDo::getCreateTime, query.getCreateTimeFrom());
        w.le(query.getCreateTimeTo() != null, StockDo::getCreateTime, query.getCreateTimeTo());

        w.ge(query.getUpdateTimeFrom() != null, StockDo::getUpdateTime, query.getUpdateTimeFrom());
        w.le(query.getUpdateTimeTo() != null, StockDo::getUpdateTime, query.getUpdateTimeTo());

        w.orderByDesc(StockDo::getCreateTime);

        return w;
    }
}
